import pandas as pd
import numpy as np
from dsjxtjc_mysql import mmySQL
import math
import random
import time
class Recommend():
    def __init__(self, similarity_matrix_dir, price_profit_list_dir,
                 people_average_num_food_dir, people_average_num_type_dir,
                 sale_rank_dir, food_rate_dir):
        self.k = 30
        self.recommend_num = 200
        self.history_food_list = None
        self.similarity_matrix = pd.read_csv(similarity_matrix_dir, header=0, index_col=0)
        self.price_profit_list = pd.read_csv(price_profit_list_dir, header=0, index_col=0)
        # 列是字符串 行是int
        self.people_average_num_food = pd.read_csv(people_average_num_food_dir, header=0, index_col=0, dtype=float)
        self.people_average_num_type = pd.read_csv(people_average_num_type_dir, header=0, index_col=0,dtype=float)
        self.sale_rank = pd.read_csv(sale_rank_dir, index_col=0, header=None)
        self.food_rate = pd.read_csv(food_rate_dir, index_col=0, header=0)
        #print(self.food_rate)
        #print(type(list(self.food_rate.index)[0]))
        #print(self.people_average_num_type)
        # 列名读进来自动前边补个0 去掉---------------------------
        columns = list(self.similarity_matrix.columns)
        for i in range(len(columns)):
            if columns[i][0] == '0':
                columns[i] = columns[i][1:]
        # 行名float->str
        index = self.similarity_matrix.index
        index = [str(i) for i in list(index)]
        self.similarity_matrix.columns = columns
        self.similarity_matrix.index = index
        # ------------------------------------------------------
        index = self.price_profit_list.index
        index = [str(i) for i in list(index)]
        self.price_profit_list.index = index
        # ------------------------------------------------------
        index = self.sale_rank.index
        index = [str(i) for i in list(index)]
        self.sale_rank.index = index
        # ------------------------------------------------------


    def interest_degree(self, food_name):
        # 计算用户的感兴趣程度
        # print(food_name)
        food_num = self.history_food_list[food_name]
        food_profit = self.price_profit_list.loc[food_name, 'profit']
        #print(food_num)
        # print(food_profit)
        interest_d = math.log(food_num)
        return interest_d

    def get_item_interest(self, j, user_like_set, j_top_k_set):
        # 获得用户对于j物品的兴趣 top_k是j相似度的top_k
        intersection_set = user_like_set & j_top_k_set
        interest = 0
        for i in intersection_set:
            interest += self.similarity_matrix.loc[i, j]*self.interest_degree(i)
        return interest

    def get_recommend_list(self, history_food_list):
        #print(history_food_list)
        self.history_food_list = history_food_list
        user_like_set = set(history_food_list.index)
        items_list = list(self.similarity_matrix.columns)
        items_interest_list = pd.Series()
        for j in items_list:
            j_set = self.similarity_matrix.loc[j, :]
            #  获得和J最相近的k个物品的集合
            j_top_k_set = set(list(j_set.nlargest(self.k).index))
            j_interest = self.get_item_interest(j, user_like_set, j_top_k_set)
            items_interest_list[j] = j_interest
        recommend = items_interest_list.nlargest(self.recommend_num)
        recommend  = list(recommend.index)
        return recommend


    def get_hot_list(self):
        hot_list = pd.Series(self.sale_rank.iloc[:200, 0])
        hot_list = list(hot_list.index)
        return hot_list


    def get_recommend_profit_list(self, recommend_list):
        profit_rate = 0.5
        recommend_order = []
        for i in recommend_list:  # 去重
            if i not in recommend_order:
                recommend_order.append(i)
        recommend_order = pd.Series(index=recommend_order)
        price_order = pd.Series()
        for i in list(recommend_order.index):
            price_order[i] = self.price_profit_list.loc[i, 'profit']
        price_order = price_order.sort_values(ascending=False)
        for i in range(np.shape(recommend_order)[0]):  # 将排序换成分值
            recommend_order.iloc[i] = np.shape(recommend_order)[0] - i
            price_order.iloc[i] = np.shape(recommend_order)[0] - i
        final_order = pd.Series()
        for i in recommend_order.index:
            final_order[i] = recommend_order[i] + profit_rate * price_order[i]
        final_order = final_order.sort_values(ascending=False)
        final_order = list(final_order.index)
        return final_order


    # 将带小数的推荐菜的数量变成整数
    def get_each_class_food_num(self, people_num):
        # print(self.people_average_num_food.index)
        if people_num < 11:
            each_class_food_num = self.people_average_num_food.loc[float(people_num), :].values
        else:
            each_class_food_num = self.people_average_num_food.loc[-1, :].values*people_num
            # 人数为-1是平均每人点各类菜多少
        for i in range(len(each_class_food_num)):
            num = each_class_food_num[i]
            each_class_food_num[i] = int(int(num)+int(random.uniform(0, 1) < (num-int(num))))
        return each_class_food_num


    def get_each_class_food_type_num(self,people_num):
        if people_num < 11:
            each_class_food_type_num = self.people_average_num_type.loc[float(people_num), :].values
        else:
            each_class_food_type_num = self.people_average_num_type.loc[-1, :].values
        for i in range(len(each_class_food_type_num)):
            num = each_class_food_type_num[i]
            each_class_food_type_num[i] = int(int(num)+int(random.uniform(0, 1) < (num-int(num))))
        #print(each_class_food_type_num)
        return each_class_food_type_num

    def get_final_recommend_list(self, candidate_food, each_class_food_num):  # use in get_package
        final_recommend_list = []
        # print(candidate_food)
        # print(each_class_food_num)
        # print(sum(each_class_food_num))
        for i in range(8):  # 8大类
            each_class_food_rate = {}
            for j in candidate_food[i]:
                each_class_food_rate[j] = self.food_rate.loc[int(j), 'rate']
            total = 0
            for k, v in each_class_food_rate.items():
                total += v
            actual_food_num = 0
            for k, v in each_class_food_rate.items():
                each_class_food_rate[k] = each_class_food_rate[k]/total  # 类内物品概率归一化
                each_class_food_rate[k] = int(round(each_class_food_rate[k]*each_class_food_num[i]))  # 具体取到每个菜取几个
                actual_food_num += each_class_food_rate[k]
            # print(actual_food_num)
            # print(each_class_food_num[i])
            # 因为是四舍五入得到的结果 与目标要推荐的菜品数量有一定误差
            # 用以下步骤修正-----------------------
            if actual_food_num > each_class_food_num[i]:
                for k, v in each_class_food_rate.items():
                    each_class_food_rate[k] -= (actual_food_num - each_class_food_num[i])
                    break
            if actual_food_num < each_class_food_num[i]:
                for k, v in each_class_food_rate.items():
                    each_class_food_rate[k] += (each_class_food_num[i]-actual_food_num)
                    break
            # -------------------------------------

            for k, v in each_class_food_rate.items():
                for _ in range(int(v)):
                    final_recommend_list.append(k)
            #print(final_recommend_list)

        return final_recommend_list


    def get_package(self, raw_recommend_list, people_num):  # 生成套餐
        # 将带小数的推荐菜的数量变成整数
        raw_recommend_list = pd.Series(index=raw_recommend_list)
        recommend_list_in_diffclass = []
        for i in range(10):
            recommend_list_in_diffclass.append([])
        for i in raw_recommend_list.index:
            if len(i) == 5:
                c = i[0]
            else:
                c = i[0:2]
            recommend_list_in_diffclass[int(c)-1].append(i)
        recommend_list_in_diffclass.pop() # 不推荐第九类 第10类
        recommend_list_in_diffclass.pop()
        each_class_food_num = self.get_each_class_food_num(people_num)  # 每类菜推荐几个
        # each_class_food_type_num = [2, 2, 6, 15, 15, 5, 3, 2]
        each_class_food_type_num = self.get_each_class_food_type_num(people_num)  # 每类菜推荐的种类数
        candidate_food = []  # 候选菜品
        #print(recommend_list_in_diffclass)
        for i in range(8):
            candidate_food.append(recommend_list_in_diffclass[i][:int(each_class_food_type_num[i])])
        final_recommend_list = self.get_final_recommend_list(candidate_food, each_class_food_num)
        return final_recommend_list

    def run(self,sql_obj, phone_num, customer_num):
        history_food_list = get_people_history_foods(sql_obj, 'qinghua_huiyuan_caipin', phone_num)
        print_history_food_list = []
        for i in list(history_food_list.index):
            for j in range(history_food_list[i]):
                print_history_food_list.append(i)
        #print(history_food_list)
        #print(print_history_food_list)
        if list(history_food_list) == []:  # 如果没找到记录 那就按热门的推荐
            recommend_list = recommend_obj.get_hot_list()
            #print("new user")
        else:
            recommend_list = self.get_recommend_list(history_food_list)  # 通过推荐算法得到推荐列表
            #print("old user")
        #print("recommend_list: ", recommend_list)
        recommend_profit_list = self.get_recommend_profit_list(recommend_list)  # 结合利润得到推荐列表
        #print("recommend_profit_list: ", recommend_profit_list)
        food_package = self.get_package(recommend_profit_list, customer_num)
        return food_package


def get_people_history_foods(sql_obj,table,phone_num):
    FOOD_INDEX=4
    select_order = "SELECT * FROM {} WHERE mem_mobile='{}';".format(table,phone_num)
    result = sql_obj.select_data(select_order)
    history_food_list = []
    for i in result:
        if(len(i[4])!=2):  # 有的菜号是两位
            if(i[4][0]=='0'):
                history_food_list.append(i[4][1:])
            else:
                history_food_list.append(i[4])
    history_food_list = pd.value_counts(pd.Series(history_food_list))
    return history_food_list



if __name__ == '__main__':
    t1 = time.time()
    phone_num = '13783431022'
    # test_set_i = set(['20202', '81383', '20201', '10101', '40402', '30314', '30312', '30311', '50507', '30313'])
    # test_set_j = set(['30314', '30312', '30311', '50507', '30313', '60601', '40401', '50514', '81474', '30310'])
    sql_obj = mmySQL("localhost", "root", "123456789", "dsjxtjc")
    recommend_obj = Recommend('../data/similarity_matrix.csv', '../data/item_price.csv',
                              '../data/people_average_num_food.csv', '../data/people_average_num_type.csv',
                              '../data/sale_rank.csv', '../data/food_rate.csv')

    recommend_profit_list = recommend_obj.run(sql_obj, recommend_obj)
    
    print("food_package: ", food_package)
    print(time.time()-t1)

